/**
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

package pittypat.web;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import pittypat.*;
import pittypat.configuration.ConfigException;

import java.beans.PropertyVetoException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;

/**
 * 处理用户登录、注销、用户信息查询以及用户导航列表查询。
 * <p>
 * 支持 "/user/info"、"/user/login"、"/user/logout"、"/user/navlist"
 * 四个路径的处理，所有其他路径一律相应 404（Not Found）。
 * <p>
 * 如果不是登录请求，那么此前必须已经通过 RequestContext.setupForServlet
 * 设置了当前请求上下文，并且请求上下文中包含了当前用户的用户令牌。
 */
final class UserHandler {
	// 客户端发送的客户端是否支持 COOKIE 的指示参数的名称，当该参数设置为 true 时，登录成功后用户令牌会被拆分为两个部分，一部分保存在
	// COOKIE 中，另外一部分通过响应正文发送到客户端，客户端可以将其保存在 localStorage 或其他更安全的位置
	private static final String COOKIE_ENABLED_PARAM_NAME = "__cookie_enabled";

	private UserHandler() {
	}

	/**
	 * 处理用户登录、注销、用户信息查询和用户导航列表查询。
	 * 
	 * @param request
	 *            当前请求对象。
	 * @param response
	 *            当前响应对象。
	 * 
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws ClassNotFoundException
	 * @throws PropertyVetoException
	 * @throws ConfigException
	 * @throws SQLException
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws InvocationTargetException
	 * @throws IllegalArgumentException
	 */
	public static void handle(String path, HttpServletRequest request, HttpServletResponse response)
			throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, SQLException,
			ConfigException, PropertyVetoException {
		if (path.equalsIgnoreCase("/user/info")) {
			// 处理查询用户信息的请求。
			processUserInfo(request, response);
		} else if (path.equalsIgnoreCase("/user/login")) {
			// 处理用户登录的请求。
			processLogin(request, response);
		} else if (path.equalsIgnoreCase("/user/logout")) {
			// 处理用户注销的请求。
			processLogout(request, response);
		} else if (path.equalsIgnoreCase("/user/navlist")) {
			// 处理查询用户导航列表的请求。
			processNavList(request, response);
		} else {
			// 不受支持的请求路径。
			response.sendError(404, "Not Found");
		}
	}

	/**
	 * 处理查询用户信息的请求。
	 * 
	 * @param request
	 *            当前请求对象。
	 * @param response
	 *            当前响应对象。
	 * 
	 * @throws IOException
	 * @throws ClassNotFoundException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws SQLException
	 * @throws ConfigException
	 * @throws PropertyVetoException
	 */
	private static void processUserInfo(HttpServletRequest request, HttpServletResponse response)
			throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, SQLException,
			ConfigException, PropertyVetoException {
		// 当前用户授权服务
		IAuthorityService authService = AuthorityService.getCurrent();

		if (authService == null) {
			ResponseHelper.sendErrorMessage(response,
					"没有提供有效的用户账号服务，请检查配置文件的 authorityService 配置项目。\r\n这是一个配置错误，需要系统管理人员调整配置文件。");
			return;
		}

		// 当前用户的用户令牌
		String userToken = (String) RequestContext.getRequestContext(request).getUserToken();
		// 查询到的用户信息，默认为匿名用户
		IUser user = User.ANONYMOUS;

		if (userToken != null && userToken.length() > 0) {
			// 根据用户令牌查询用户信息
			Permission permission = authService.demand(SystemTokenProvider.getCurrent().getSystemToken(), userToken, "",
					WebAsyncProgress.DEFAULT);
			// 没有找到用户时，认为是匿名用户
			if (permission != null) {
				user = permission.getUser();
				user = user == null ? User.ANONYMOUS : user;
			}
		}

		// 向客户端发送用户信息
		ResponseHelper.sendUserMessage(response, user);
	}

	/**
	 * 处理用户登录的请求。
	 * 
	 * @param request
	 *            当前请求对象。
	 * @param response
	 *            当前响应对象。
	 * 
	 * @throws IOException
	 * @throws ClassNotFoundException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws SQLException
	 * @throws ConfigException
	 * @throws PropertyVetoException
	 */
	private static void processLogin(HttpServletRequest request, HttpServletResponse response)
			throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, SQLException,
			ConfigException, PropertyVetoException {
		// 当前用户授权服务
		IAuthorityService authService = AuthorityService.getCurrent();

		if (authService == null) {
			ResponseHelper.sendErrorMessage(response,
					"没有提供有效的用户账号服务，请检查配置文件的 authorityService 配置项目。\r\n这是一个配置错误，需要系统管理人员调整配置文件。");
			return;
		}

		// 登录名称
		String name = request.getParameter("name");

		if (name == null || name.length() == 0) {
			ResponseHelper.sendErrorMessage(response, "没有提供用户登录名称。");
			return;
		}

		// 登录密码
		String password = request.getParameter("password");

		if (password == null || password.length() == 0) {
			ResponseHelper.sendErrorMessage(response, "没有提供用户登录密码。");
			return;
		}

		// 执行登录
		LoginResult result = authService.login(SystemTokenProvider.getCurrent().getSystemToken(), name, password,
				WebAsyncProgress.DEFAULT);
		result = result == null ? LoginResult.ANONYMOUS : result;
		String token = result.getToken();

		if (result.isAnonymous()) {
			// 登录失败，通知浏览器删除 COOKIE

			Cookie cookie = new Cookie(RequestContext.USER_TOKEN_NAME, "");
			cookie.setHttpOnly(true);
			cookie.setPath("/");
			// 立即删除 COOKIE
			cookie.setMaxAge(0);
			response.addCookie(cookie);
		} else if (token.length() > 1) {
			// 检查客户端是否启用了 cookie，如果启用了 cookie，则将用户令牌分为两个部分
			boolean cookieEnabled = Boolean.parseBoolean(request.getParameter(COOKIE_ENABLED_PARAM_NAME));

			if (cookieEnabled) {
				int index = token.length() / 2;

				// COOKIE 中保存后半部分
				Cookie cookie = new Cookie(RequestContext.USER_TOKEN_NAME, token.substring(index));
				cookie.setHttpOnly(true);
				cookie.setPath("/");
				// 在浏览器关闭后删除 COOKIE
				cookie.setMaxAge(-1);
				response.addCookie(cookie);

				// localStorage 中保存前半部分
				result.setToken(token.substring(0, index));
			}
		}

		ResponseHelper.sendValueMessage(response, result);
	}

	/**
	 * 处理用户注销的请求。
	 * 
	 * @param request
	 *            当前请求对象。
	 * @param response
	 *            当前响应对象。
	 * 
	 * @throws ClassNotFoundException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws UnsupportedEncodingException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws SQLException
	 * @throws ConfigException
	 * @throws PropertyVetoException
	 */
	private static void processLogout(HttpServletRequest request, HttpServletResponse response)
			throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException,
			UnsupportedEncodingException, InvocationTargetException, NoSuchMethodException, SecurityException,
			SQLException, ConfigException, PropertyVetoException {
		// 当前用户授权服务
		IAuthorityService authService = AuthorityService.getCurrent();

		if (authService == null) {
			ResponseHelper.sendErrorMessage(response,
					"没有提供有效的用户账号服务，请检查配置文件的 authorityService 配置项目。\r\n这是一个配置错误，需要系统管理人员调整配置文件。");
			return;
		}

		// 当前用户的用户令牌
		String userToken = (String) RequestContext.getRequestContext(request).getUserToken();
		if (userToken != null && userToken.length() > 0) {
			authService.logout(SystemTokenProvider.getCurrent().getSystemToken(), userToken, WebAsyncProgress.DEFAULT);
		}

		// 写入一个已经过期的 cookie，导致客户端的 token 无效
		Cookie cookie = new Cookie(RequestContext.USER_TOKEN_NAME, "");
		cookie.setHttpOnly(true);
		cookie.setPath("/");
		// 立即删除 COOKIE
		cookie.setMaxAge(0);
		response.addCookie(cookie);
		response.setStatus(200);
	}

	/**
	 * 处理查询用户导航列表的请求。
	 * 
	 * @param request
	 *            当前请求对象。
	 * @param response
	 *            当前响应对象。
	 * 
	 * @throws IOException
	 * @throws ClassNotFoundException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws SQLException
	 * @throws ConfigException
	 * @throws PropertyVetoException
	 */
	private static void processNavList(HttpServletRequest request, HttpServletResponse response)
			throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, SQLException,
			ConfigException, PropertyVetoException {
		// 当前用户授权服务
		IAuthorityService authService = AuthorityService.getCurrent();

		if (authService == null) {
			ResponseHelper.sendErrorMessage(response,
					"没有提供有效的用户账号服务，请检查配置文件的 authorityService 配置项目。\r\n这是一个配置错误，需要系统管理人员调整配置文件。");
			return;
		}

		// 当前用户的用户令牌
		String userToken = (String) RequestContext.getRequestContext(request).getUserToken();
		NavList list = NavList.EMPTY;
		if (userToken != null && userToken.length() > 0) {
			list = authService.navList(SystemTokenProvider.getCurrent().getSystemToken(), userToken,
					WebAsyncProgress.DEFAULT);
		}

		// 发送用户导航列表
		ResponseHelper.sendValueMessage(response, list == null ? NavList.EMPTY : list);
	}
}
